<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>程序员小航</title><meta name="author" content="liuzhihang"><meta name="copyright" content="liuzhihang"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="学，然后知不足；教，然后知困。">
<meta property="og:type" content="website">
<meta property="og:title" content="程序员小航">
<meta property="og:url" content="https://liuzhihang.com/page/2/index.html">
<meta property="og:site_name" content="程序员小航">
<meta property="og:description" content="学，然后知不足；教，然后知困。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/404/avatar.jpg">
<meta property="article:author" content="liuzhihang">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/404/avatar.jpg"><link rel="shortcut icon" href="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/favicon.ico"><link rel="canonical" href="https://liuzhihang.com/page/2/"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//www.google-analytics.com" crossorigin=""/><link rel="preconnect" href="//pingjs.qq.com"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><meta name="google_site_verification" content="GZshAnnrC-4eCrl5h-e_5Rdk4lOUhRK7ShULoRi2q0E"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><script async="async" src="https://www.googletagmanager.com/gtag/js?id=UA-126394362-1"></script><script>window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-126394362-1');
</script><script>var _mtac = {};
(function() {
    var mta = document.createElement("script");
    mta.src = "//pingjs.qq.com/h5/stats.js?v2.0.4";
    mta.setAttribute("name", "MTAH5");
    mta.setAttribute("sid", "66540676");
    var s = document.getElementsByTagName("script")[0];
    s.parentNode.insertBefore(mta, s);
})();
</script><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: {"path":"search.xml","languages":{"hits_empty":"找不到您查询的内容：${query}"}},
  translate: {"defaultEncoding":2,"translateDelay":0,"msgToTraditionalChinese":"繁","msgToSimplifiedChinese":"簡"},
  noticeOutdate: {"limitDay":730,"position":"top","messagePrev":"It has been","messageNext":"days since the last update, the content of the article may be outdated."},
  highlight: undefined,
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    jQuery: 'https://cdn.jsdelivr.net/npm/jquery@latest/dist/jquery.min.js',
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/justifiedGallery/dist/js/jquery.justifiedGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/justifiedGallery/dist/css/justifiedGallery.min.css'
    },
    fancybox: {
      js: 'https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.js',
      css: 'https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: true,
  isanchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: '程序员小航',
  isPost: false,
  isHome: true,
  isHighlightShrink: false,
  isToc: false,
  postUpdate: '2021-09-08 17:05:44'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if (GLOBAL_CONFIG_SITE.isHome && /iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><link rel="stylesheet" href="//at.alicdn.com/t/font_1608878_xsf93f0c1a.css"><meta name="generator" content="Hexo 5.2.0"><link rel="alternate" href="/atom.xml" title="程序员小航" type="application/atom+xml">
</head><body><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src= "https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/loading.gif" data-lazy-src="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/404/avatar.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="site-data"><div class="data-item is-center"><div class="data-item-link"><a href="/archives/"><div class="headline">文章</div><div class="length-num">145</div></a></div></div><div class="data-item is-center"><div class="data-item-link"><a href="/tags/"><div class="headline">标签</div><div class="length-num">55</div></a></div></div><div class="data-item is-center"><div class="data-item-link"><a href="/categories/"><div class="headline">分类</div><div class="length-num">31</div></a></div></div></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="javascript:void(0);"><i class="fa-fw fas fa-layer-group"></i><span> 导航</span><i class="fas fa-chevron-down expand hide"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></li><li><a class="site-page child" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></li><li><a class="site-page child" href="/categories/"><i class="fa-fw fas fa-bookmark"></i><span> 分类</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 推荐</span><i class="fas fa-chevron-down expand hide"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/books/"><i class="fa-fw fas fa-book"></i><span> 书单</span></a></li><li><a class="site-page child" href="/movies/"><i class="fa-fw fas fa-film"></i><span> 电影</span></a></li><li><a class="site-page child" href="/games/"><i class="fa-fw fas fa-gamepad"></i><span> 游戏</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/contact/"><i class="fa-fw fas fa-comments"></i><span> 留言板</span></a></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fas fa-address-book"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-user-circle"></i><span> 关于</span></a></div><div class="menus_item"><a class="site-page" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 其他</span><i class="fas fa-chevron-down expand hide"></i></a><ul class="menus_item_child"><li><a class="site-page child" target="_blank" rel="noopener" href="https://juejin.im/user/1987506650493117"><i class="fa-fw iconfont iconjuejin-1"></i><span> 掘金</span></a></li><li><a class="site-page child" target="_blank" rel="noopener" href="https://www.zhihu.com/people/liuzhihang"><i class="fa-fw iconfont iconzhihu1"></i><span> 知乎</span></a></li><li><a class="site-page child" target="_blank" rel="noopener" href="https://www.infoq.cn/profile/00B8AE08DA916E/publish"><i class="fa-fw iconfont iconweibiaoti-1"></i><span> InfoQ</span></a></li><li><a class="site-page child" target="_blank" rel="noopener" href="https://blog.csdn.net/qq_36535538"><i class="fa-fw iconfont iconcsdn"></i><span> CSDN</span></a></li><li><a class="site-page child" target="_blank" rel="noopener" href="https://leetcode-cn.com/u/liuzhihang/"><i class="fa-fw iconfont iconleetcode"></i><span> LeetCode</span></a></li></ul></div></div></div></div><div class="page" id="body-wrap"><header class="not-top-img" id="page-header"><nav id="nav"><span id="blog_name"><a id="site-name" href="/">程序员小航</a></span><div id="menus"><div id="search-button"><a class="site-page social-icon search"><i class="fas fa-search fa-fw"></i><span> 搜索</span></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="javascript:void(0);"><i class="fa-fw fas fa-layer-group"></i><span> 导航</span><i class="fas fa-chevron-down expand hide"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></li><li><a class="site-page child" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></li><li><a class="site-page child" href="/categories/"><i class="fa-fw fas fa-bookmark"></i><span> 分类</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 推荐</span><i class="fas fa-chevron-down expand hide"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/books/"><i class="fa-fw fas fa-book"></i><span> 书单</span></a></li><li><a class="site-page child" href="/movies/"><i class="fa-fw fas fa-film"></i><span> 电影</span></a></li><li><a class="site-page child" href="/games/"><i class="fa-fw fas fa-gamepad"></i><span> 游戏</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/contact/"><i class="fa-fw fas fa-comments"></i><span> 留言板</span></a></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fas fa-address-book"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-user-circle"></i><span> 关于</span></a></div><div class="menus_item"><a class="site-page" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 其他</span><i class="fas fa-chevron-down expand hide"></i></a><ul class="menus_item_child"><li><a class="site-page child" target="_blank" rel="noopener" href="https://juejin.im/user/1987506650493117"><i class="fa-fw iconfont iconjuejin-1"></i><span> 掘金</span></a></li><li><a class="site-page child" target="_blank" rel="noopener" href="https://www.zhihu.com/people/liuzhihang"><i class="fa-fw iconfont iconzhihu1"></i><span> 知乎</span></a></li><li><a class="site-page child" target="_blank" rel="noopener" href="https://www.infoq.cn/profile/00B8AE08DA916E/publish"><i class="fa-fw iconfont iconweibiaoti-1"></i><span> InfoQ</span></a></li><li><a class="site-page child" target="_blank" rel="noopener" href="https://blog.csdn.net/qq_36535538"><i class="fa-fw iconfont iconcsdn"></i><span> CSDN</span></a></li><li><a class="site-page child" target="_blank" rel="noopener" href="https://leetcode-cn.com/u/liuzhihang/"><i class="fa-fw iconfont iconleetcode"></i><span> LeetCode</span></a></li></ul></div></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav></header><main class="layout" id="content-inner"><div class="recent-posts" id="recent-posts"><div class="recent-post-item"><div class="post_cover left_radius"><a href="/2021/07/04/redisson-7.html" title="Redisson 分布式锁源码 07：公平锁释放">     <img class="post_bg" src= "https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/loading.gif" data-lazy-src="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/jOx74t-WsCHCL.png" onerror="this.onerror=null;this.src='https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/feature/92776_n5aac6.jpg'" alt="Redisson 分布式锁源码 07：公平锁释放"></a></div><div class="recent-post-info"><a class="article-title" href="/2021/07/04/redisson-7.html" title="Redisson 分布式锁源码 07：公平锁释放">Redisson 分布式锁源码 07：公平锁释放</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2021-07-04T13:30:30.000Z" title="发表于 2021-07-04 21:30:30">2021-07-04</time><span class="article-meta__separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2021-09-08T08:49:04.206Z" title="更新于 2021-09-08 16:49:04">2021-09-08</time></span><span class="article-meta"><span class="article-meta__separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/redisson/">redisson</a></span></div><div class="content">前言看门狗机制是在 RedissonBaseLock#scheduleExpirationRenewal 方法中，这块公平锁和非公平锁并无区别。
前文已经了解到，公平锁加锁失败之后，会将当前放到等待队列中，通过 Java 代码中的循环不断尝试获得锁。
锁释放主动释放源码：RedissonFairLock#unlockInnerAsync

KEYS[1]：加锁的名字，anyLock；
KEYS[2]：加锁等待队列，redisson_lock_queue:&#123;anyLock&#125;；
KEYS[3]：等待队列中线程锁时间的 set 集合，redisson_lock_timeout:&#123;anyLock&#125;，是按照锁的时间戳存放到集合中的；
KEYS[4]：redisson_lock__channel:&#123;anyLock&#125;；
ARGV[1]：LockPubSub.UNLOCK_MESSAGE；
ARGV[2]：锁超时时间 30000；
ARGV[3]：UUID:ThreadId 组合 58f6c4a2-9908-4957-b229-283a453 ...</div></div></div><div class="recent-post-item"><div class="post_cover right_radius"><a href="/2021/07/03/redisson-6.html" title="Redisson 分布式锁源码 06：公平锁排队加锁">     <img class="post_bg" src= "https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/loading.gif" data-lazy-src="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/jOx74t-WsCHCL.png" onerror="this.onerror=null;this.src='https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/feature/92776_n5aac6.jpg'" alt="Redisson 分布式锁源码 06：公平锁排队加锁"></a></div><div class="recent-post-info"><a class="article-title" href="/2021/07/03/redisson-6.html" title="Redisson 分布式锁源码 06：公平锁排队加锁">Redisson 分布式锁源码 06：公平锁排队加锁</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2021-07-03T13:30:30.000Z" title="发表于 2021-07-03 21:30:30">2021-07-03</time><span class="article-meta__separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2021-09-08T08:49:04.198Z" title="更新于 2021-09-08 16:49:04">2021-09-08</time></span><span class="article-meta"><span class="article-meta__separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/redisson/">redisson</a></span></div><div class="content">前言在上一篇文章中已经分析过公平锁的加锁源码，并得出结论：

Redis Hash 数据结构：存放当前锁，Redis Key 就是锁，Hash 的 field 是加锁线程，Hash 的 value 是 重入次数；
Redis List 数据结构：充当线程等待队列，新的等待线程会使用 rpush 命令放在队列右边；
Redis sorted set 有序集合数据结构：存放等待线程的顺序，分数 score 用来是等待线程的超时时间戳。

现在看一下加锁失败被放到等待队列之后，线程是如何处理的？
排队等锁源码入口：org.redisson.RedissonLock#lock(long, java.util.concurrent.TimeUnit, boolean)。
线程进入排队之后，在 Java 代码中会 while (true) 一直循环调用 tryAcquire，尝试获取锁。
最终还是来到 RedissonFairLock#tryLockInnerAsync 方法中。
方便起见，重新贴一下 Lua 脚本，以及脚本的参数含义。

KEYS[1]：加锁的名字，anyLock；
KEYS[2 ...</div></div></div><div class="recent-post-item"><div class="post_cover left_radius"><a href="/2021/07/02/redisson-5.html" title="Redisson 分布式锁源码 05：公平锁加锁">     <img class="post_bg" src= "https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/loading.gif" data-lazy-src="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/gGANu9-rQMFDI.png" onerror="this.onerror=null;this.src='https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/feature/92776_n5aac6.jpg'" alt="Redisson 分布式锁源码 05：公平锁加锁"></a></div><div class="recent-post-info"><a class="article-title" href="/2021/07/02/redisson-5.html" title="Redisson 分布式锁源码 05：公平锁加锁">Redisson 分布式锁源码 05：公平锁加锁</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2021-07-02T13:30:30.000Z" title="发表于 2021-07-02 21:30:30">2021-07-02</time><span class="article-meta__separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2021-09-08T08:49:04.196Z" title="更新于 2021-09-08 16:49:04">2021-09-08</time></span><span class="article-meta"><span class="article-meta__separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/redisson/">redisson</a></span></div><div class="content">前言默认的加锁逻辑是非公平的。
在加锁失败时，线程会进入 while 循环，一直尝试获得锁，这时候是多线程进行竞争。就是说谁抢到就是谁的。
Redisson 提供了 公平锁 机制，使用方式如下：
RLock fairLock = redisson.getFairLock(&quot;anyLock&quot;);
// 最常见的使用方法
fairLock.lock();
下面一起看下公平锁是如何实现的？
公平锁相信小伙伴们看过前面的文章，已经轻车熟路了，直接定位到源码方法：RedissonFairLock#tryLockInnerAsync。
好家伙，这一大块代码，我截图也截不完，咱们直接分析 lua 脚本。

PS：虽然咱不懂 lua，但是这一堆堆的 if else 咱们大概还是能看懂的。

因为 debug 发现 command == RedisCommands.EVAL_LONG，所以直接看下面一部分。

这么长，连呼好几声好家伙！
先来看看参数都有啥？

KEYS[1]：加锁的名字，anyLock；
KEYS[2]：加锁等待队列，redisson_lock_queue:&#12 ...</div></div></div><div class="recent-post-item"><div class="post_cover right_radius"><a href="/2021/07/01/redisson-4.html" title="Redisson 分布式锁源码 04：可重入锁释放">     <img class="post_bg" src= "https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/loading.gif" data-lazy-src="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/lWwK5I-bypFT5.png" onerror="this.onerror=null;this.src='https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/feature/92776_n5aac6.jpg'" alt="Redisson 分布式锁源码 04：可重入锁释放"></a></div><div class="recent-post-info"><a class="article-title" href="/2021/07/01/redisson-4.html" title="Redisson 分布式锁源码 04：可重入锁释放">Redisson 分布式锁源码 04：可重入锁释放</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2021-07-01T13:30:30.000Z" title="发表于 2021-07-01 21:30:30">2021-07-01</time><span class="article-meta__separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2021-09-08T08:49:04.216Z" title="更新于 2021-09-08 16:49:04">2021-09-08</time></span><span class="article-meta"><span class="article-meta__separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/redisson/">redisson</a></span></div><div class="content">前言前面已经了解到了，可重入锁加锁，看门狗以及锁的互斥阻塞。
当锁加锁成功之后，锁是如何释放的？
主动释放源码入口：RedissonLock#unlock

在解锁时会获取当前线程的id。
一路往里跟，直接来到 RedissonLock#unlockInnerAsync：

分析一下 lua 脚本的内容：


如果锁不存在，直接返回 null；
如果锁存在，则对锁的重入次数 -1；
剩余重入次数大于 0，重新设置过期时间，返回 0；
剩余重入次数不大于 0，删除 redis key 并发布消息，返回 1；



主动释放锁这块考虑的不仅仅是对 key 进行处理，因为可能存在重入锁，所以会先对 redis key 对应的 hash value 进行递减，相当于减去重入次数。
自动释放相比较主动释放，自动释放就比较容易理解了。

当服务宕机时，看门狗不再看门，那么最多 30s 之后锁被自动释放；
当设置锁的时间时，锁到了时间，自动释放。

总结
Redisson 锁的释放分为两种：

主动释放：自己调用 API unlock 即可；
宕机/到期自动释放：Redis key 指定时间自动过期 ...</div></div></div><div class="recent-post-item"><div class="post_cover left_radius"><a href="/2021/06/30/redisson-3.html" title="Redisson 分布式锁源码 03：可重入锁互斥">     <img class="post_bg" src= "https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/loading.gif" data-lazy-src="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/KjiQ4g-EFrw6t.png" onerror="this.onerror=null;this.src='https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/feature/92776_n5aac6.jpg'" alt="Redisson 分布式锁源码 03：可重入锁互斥"></a></div><div class="recent-post-info"><a class="article-title" href="/2021/06/30/redisson-3.html" title="Redisson 分布式锁源码 03：可重入锁互斥">Redisson 分布式锁源码 03：可重入锁互斥</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2021-06-30T13:30:30.000Z" title="发表于 2021-06-30 21:30:30">2021-06-30</time><span class="article-meta__separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2021-09-08T08:49:04.204Z" title="更新于 2021-09-08 16:49:04">2021-09-08</time></span><span class="article-meta"><span class="article-meta__separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/redisson/">redisson</a></span></div><div class="content">前言看过可重入锁的 Lua 脚本，已经可以知道当锁存在时，是会加锁失败的。
下面看一下，加锁失败之后是如何处理的呢？
加锁 Lua 脚本
在 lua 脚本中，前两段 if 分别排除了两种情况：

锁不存在；
锁存在且是自己线程（可重入）；

剩下的情况就是锁存在，但是不是自己，也就意味着加锁失败。
执行 pttl 命令，返回锁的剩余时间。
加锁失败后的处理源码定位：org.redisson.RedissonLock#lock(long, java.util.concurrent.TimeUnit, boolean)

先来看开头一部分：
加锁成功后，会返回 ttl，此处会判断为 null，直接返回。
所以，下面的部分就是当获取锁失败之后的逻辑。

忽略掉不需要很关注的逻辑，重点则是 while (true) 里面这一小块。
一直循环调用 tryAcquire 方法，直到加锁成功！
总结
可重入锁的互斥是依靠 Redis Lua 脚本来保证的；
加锁失败会返回当前锁的剩余时间；
加锁失败后，会在 Java 代码中使用 while 循环一直尝试加锁。

大概的流程，如下图：

相关推荐
R ...</div></div></div><div class="recent-post-item"><div class="post_cover right_radius"><a href="/2021/06/29/redisson-2.html" title="Redisson 分布式锁源码 02：看门狗">     <img class="post_bg" src= "https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/loading.gif" data-lazy-src="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/tJb1FR-W0Dm6G.png" onerror="this.onerror=null;this.src='https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/feature/92776_n5aac6.jpg'" alt="Redisson 分布式锁源码 02：看门狗"></a></div><div class="recent-post-info"><a class="article-title" href="/2021/06/29/redisson-2.html" title="Redisson 分布式锁源码 02：看门狗">Redisson 分布式锁源码 02：看门狗</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2021-06-29T13:30:30.000Z" title="发表于 2021-06-29 21:30:30">2021-06-29</time><span class="article-meta__separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2021-09-08T08:49:04.214Z" title="更新于 2021-09-08 16:49:04">2021-09-08</time></span><span class="article-meta"><span class="article-meta__separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/redisson/">redisson</a></span></div><div class="content">前言说起 Redisson，比较耳熟能详的就是这个看门狗（Watchdog）机制。
本文就一起看看加锁成功之后的看门狗（Watchdog）是如何实现的？
加锁成功在前一篇文章中介绍了可重入锁加锁的逻辑，其中 RedissonLock#tryAcquireAsync 方法是进行异步加锁的逻辑。

回顾一下这个方法的入参：

waitTime：-1；
leaseTime：-1，加锁时未指定锁时间，则为 -1，如果指定，则是指定的时间；
unit：null；
threadId：当前线程 id。

其中的 tryLockInnerAsync 在之前已经介绍过了。
当加锁成功时，会返回 null，加锁失败，会返回当前锁的剩余时间。
所以这块会进入到红框标记的部分。

leaseTime 为加锁时间，默认不指定，所以会进入到 scheduleExpirationRenewal 方法，也就是今天的主题：看门狗。
至此可以得出一个结论：
Redisson 看门狗（Watchdog）在指定加锁时间时，是不会对锁时间自动续租的。
看门狗
看门狗的一部分重点逻辑就在 renewExpiration 方法这里 ...</div></div></div><div class="recent-post-item"><div class="post_cover left_radius"><a href="/2021/06/28/redisson-1.html" title="Redisson 分布式锁源码 01：可重入锁加锁">     <img class="post_bg" src= "https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/loading.gif" data-lazy-src="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/Gu9dFF-SSFLo2.png" onerror="this.onerror=null;this.src='https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/feature/92776_n5aac6.jpg'" alt="Redisson 分布式锁源码 01：可重入锁加锁"></a></div><div class="recent-post-info"><a class="article-title" href="/2021/06/28/redisson-1.html" title="Redisson 分布式锁源码 01：可重入锁加锁">Redisson 分布式锁源码 01：可重入锁加锁</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2021-06-28T13:30:30.000Z" title="发表于 2021-06-28 21:30:30">2021-06-28</time><span class="article-meta__separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2021-09-08T08:49:04.209Z" title="更新于 2021-09-08 16:49:04">2021-09-08</time></span><span class="article-meta"><span class="article-meta__separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/redisson/">redisson</a></span></div><div class="content">前言相信小伙伴都是使用分布式服务，那一定绕不开分布式服务中数据并发更新问题！
单系统很容易想到 Java 的各种锁，像 synchronize、ReentrantLock 等等等，那分布式系统如何处理？
当然是使用分布式锁。
如果小伙伴不知道什么是分布式锁，那推荐看看石杉老师的突击课或者在网上搜一搜相关资料。
当使用 Redis 作为分布式锁时，当前使用较多的框架就是 Redisson。
当然 Redisson 也不仅仅只能当做锁来使用，也有很多其他的功能，小伙伴们可以看一看官方文档，自己多动手实践一下。
下面就开始记录 Redisson 的相关笔记！错误之处，欢迎指正。🙏🏻
环境配置
本地环境搭建的伪集群：



redisson 3.15.6

不同版本可能会有所不同，但是核心思想不会发生太大变化，如果变化很大，希望可以留言。
&lt;dependency&gt;
    &lt;groupId&gt;org.redisson&lt;/groupId&gt;
    &lt;artifactId&gt;redisson&lt;/artifactId&gt;
    &lt;ve ...</div></div></div><div class="recent-post-item"><div class="post_cover right_radius"><a href="/2021/06/07/ides-plugin-toolkit-lombok.html" title="Toolkit Json 转实体也可以支持 lombok 了！">     <img class="post_bg" src= "https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/loading.gif" data-lazy-src="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/dbzZgg-zDjPI7.jpg" onerror="this.onerror=null;this.src='https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/feature/92776_n5aac6.jpg'" alt="Toolkit Json 转实体也可以支持 lombok 了！"></a></div><div class="recent-post-info"><a class="article-title" href="/2021/06/07/ides-plugin-toolkit-lombok.html" title="Toolkit Json 转实体也可以支持 lombok 了！">Toolkit Json 转实体也可以支持 lombok 了！</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2021-06-07T11:30:30.000Z" title="发表于 2021-06-07 19:30:30">2021-06-07</time><span class="article-meta__separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2021-06-08T03:00:08.738Z" title="更新于 2021-06-08 11:00:08">2021-06-08</time></span><span class="article-meta"><span class="article-meta__separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/IDEA/">IDEA</a></span></div><div class="content">前言近日小伙伴提建议希望 Toolkit 支持 lombok。
个人认为 GsonFormat 不香么？

我没使用 GsonFormat，所以为什么不能用的，我也没法给评价。
不过，既然是小伙伴的需求，那还是得加的！

功能介绍
下载地址
在官方插件库搜索 Toolkit
github：https://github.com/liuzhihang/toolkit/releases

</div></div></div><div class="recent-post-item"><div class="post_cover left_radius"><a href="/2021/06/07/mysql-next-key-4.html" title="MySQL next-key lock 加锁范围总结">     <img class="post_bg" src= "https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/loading.gif" data-lazy-src="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/ZnytZB-FhqOd8.jpg" onerror="this.onerror=null;this.src='https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/feature/92776_n5aac6.jpg'" alt="MySQL next-key lock 加锁范围总结"></a></div><div class="recent-post-info"><a class="article-title" href="/2021/06/07/mysql-next-key-4.html" title="MySQL next-key lock 加锁范围总结">MySQL next-key lock 加锁范围总结</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2021-06-07T11:30:30.000Z" title="发表于 2021-06-07 19:30:30">2021-06-07</time><span class="article-meta__separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2021-06-08T03:06:12.243Z" title="更新于 2021-06-08 11:06:12">2021-06-08</time></span><span class="article-meta"><span class="article-meta__separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/mysql/">mysql</a></span></div><div class="content">前言三篇文章分别通过实际操作，介绍了主键、非主键唯一索引、普通索引、普通字段四个方面介绍了加锁的范围。
本篇文章再做一个总结。
data_locksselect * from performance_schema.data_locks;



LOCK_MODE
LOCK_DATA
锁范围



X,REC_NOT_GAP
15
15 那条数据的行锁


X,GAP
15
15 那条数据之前的间隙，不包含 15


X
15
15 那条数据的间隙，包含 15



LOCK_MODE = X 是前开后闭区间；
X,GAP 是前开后开区间（间隙锁）；
X,REC_NOT_GAP 行锁。

这个单独介绍，是希望我理解的没有错误，如果大佬看到了，错误之处一定要帮忙指正出来。
主键索引
加锁时，会先给表添加意向锁，IX 或 IS；
加锁是如果是多个范围，是分开加了多个锁，每个范围都有锁；（这个可以实践下 id &lt; 20 的情况）
主键等值查询，数据存在时，会对该主键索引的值加行锁 X,REC_NOT_GAP；
主键等值查询，数据不存在时，会对查询条件主键值所在的间隙添加间隙锁 X,GAP ...</div></div></div><div class="recent-post-item"><div class="post_cover right_radius"><a href="/2021/06/06/mysql-next-key-3.html" title="MySQL普通索引的加锁">     <img class="post_bg" src= "https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/loading.gif" data-lazy-src="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/ZnytZB-FhqOd8.jpg" onerror="this.onerror=null;this.src='https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/feature/92776_n5aac6.jpg'" alt="MySQL普通索引的加锁"></a></div><div class="recent-post-info"><a class="article-title" href="/2021/06/06/mysql-next-key-3.html" title="MySQL普通索引的加锁">MySQL普通索引的加锁</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2021-06-06T13:30:30.000Z" title="发表于 2021-06-06 21:30:30">2021-06-06</time><span class="article-meta__separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2021-06-08T03:06:10.758Z" title="更新于 2021-06-08 11:06:10">2021-06-08</time></span><span class="article-meta"><span class="article-meta__separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/mysql/">mysql</a></span></div><div class="content">前言前面已经介绍了主键索引的加锁范围和非主键唯一索引的加锁范围。
主键索引：

加锁时，会先给表添加意向锁，IX 或 IS；
加锁是如果是多个范围，是分开加了多个锁，每个范围都有锁；（这个可以实践下 id &lt; 20 的情况）
主键等值查询，数据存在时，会对该主键索引的值加行锁 X,REC_NOT_GAP；
主键等值查询，数据不存在时，会对查询条件主键值所在的间隙添加间隙锁 X,GAP；
主键等值查询，范围查询时情况则比较复杂：
8.0.17 版本是前开后闭，而 8.0.18 版本及以后，修改为了前开后开区间；
临界 &lt;= 查询时，8.0.17 会锁住下一个 next-key 的前开后闭区间，而 8.0.18 及以后版本，修复了这个 bug。



非主键唯一索引:

非主键唯一索引等值查询，数据存在，for update 是会在主键加锁的，而 for share 只有在走覆盖索引的情况下，会仅在自己索引上加锁；
非主键索引等值查询，数据不存在，无论是否索引覆盖，相当于一个范围查询，仅仅会在非主键索引上加锁，加的还是间隙锁，前开后开区间； 
在非主键唯一索引范围查询时，不是覆 ...</div></div></div><div class="recent-post-item"><div class="post_cover left_radius"><a href="/2021/06/06/mysql-next-key-2.html" title="看来，MySQL next-key lock 的 bug 并没有被修复！">     <img class="post_bg" src= "https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/loading.gif" data-lazy-src="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/ZnytZB-FhqOd8.jpg" onerror="this.onerror=null;this.src='https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/feature/92776_n5aac6.jpg'" alt="看来，MySQL next-key lock 的 bug 并没有被修复！"></a></div><div class="recent-post-info"><a class="article-title" href="/2021/06/06/mysql-next-key-2.html" title="看来，MySQL next-key lock 的 bug 并没有被修复！">看来，MySQL next-key lock 的 bug 并没有被修复！</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2021-06-06T12:30:30.000Z" title="发表于 2021-06-06 20:30:30">2021-06-06</time><span class="article-meta__separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2021-06-08T03:06:05.069Z" title="更新于 2021-06-08 11:06:05">2021-06-08</time></span><span class="article-meta"><span class="article-meta__separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/mysql/">mysql</a></span></div><div class="content">前言在上一篇文章《MySQL next-key lock 加锁范围是什么？》中已经介绍了主键索引的加锁范围，现在来回顾一下：

加锁时，会先给表添加意向锁，IX 或 IS；
加锁是如果是多个范围，是分开加了多个锁，每个范围都有锁；（这个可以实践下 id &lt; 20 的情况）
主键等值查询，数据存在时，会对该主键索引的值加行锁 X,REC_NOT_GAP；
主键等值查询，数据不存在时，会对查询条件主键值所在的间隙添加间隙锁 X,GAP；
主键等值查询，范围查询时情况则比较复杂：
8.0.17 版本是前开后闭，而 8.0.18 版本及以后，修改为了前开后开区间；
临界 &lt;= 查询时，8.0.17 会锁住下一个 next-key 的前开后闭区间，而 8.0.18 及以后版本，修复了这个 bug。



这篇文章会对非主键唯一索引进行操作实践。
数据库表数据CREATE TABLE `t` (
  `id` int NOT NULL COMMENT &#39;主键&#39;,
  `a` int DEFAULT NULL COMMENT &#39;唯一索引&#39;,
  `c` i ...</div></div></div><div class="recent-post-item"><div class="post_cover right_radius"><a href="/2021/06/06/docker-mysql.html" title="使用 Docker 安装并连接 MySQL">     <img class="post_bg" src= "https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/loading.gif" data-lazy-src="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/kiUEMZ-BRFqnd.png" onerror="this.onerror=null;this.src='https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/feature/92776_n5aac6.jpg'" alt="使用 Docker 安装并连接 MySQL"></a></div><div class="recent-post-info"><a class="article-title" href="/2021/06/06/docker-mysql.html" title="使用 Docker 安装并连接 MySQL">使用 Docker 安装并连接 MySQL</a><div class="article-meta-wrap"><span class="post-meta-date"><i class="far fa-calendar-alt"></i><span class="article-meta-label">发表于</span><time class="post-meta-date-created" datetime="2021-06-06T11:30:30.000Z" title="发表于 2021-06-06 19:30:30">2021-06-06</time><span class="article-meta__separator">|</span><i class="fas fa-history"></i><span class="article-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2021-06-08T03:00:46.888Z" title="更新于 2021-06-08 11:00:46">2021-06-08</time></span><span class="article-meta"><span class="article-meta__separator">|</span><i class="fas fa-inbox"></i><a class="article-meta__categories" href="/categories/docker/">docker</a></span></div><div class="content">前言作为开发，在本机捣鼓一下 Docker 还是很有必要的，本篇文章介绍如何使用 Docker 安装 MySQL，并在终端（iTerm2）使用命令连接 MySQL。
安装 MySQL核心命令如下：
# 用 8.0.17 版本举例
docker pull mysql:8.0.17

# 运行 mysql
docker run -itd --name mysql8.0.17 -p 23306:3306 -e MYSQL_ROOT_PASSWORD=root mysql:8.0.17

docker ps

登录 MySQL# 进入容器
docker exec -it mysql8.0.17 bash
# 登录 mysql
mysql -u root -p

</div></div></div><nav id="pagination"><div class="pagination"><a class="extend prev" rel="prev" href="/"><i class="fas fa-chevron-left fa-fw"></i></a><a class="page-number" href="/">1</a><span class="page-number current">2</span><a class="page-number" href="/page/3/#content-inner">3</a><span class="space">&hellip;</span><a class="page-number" href="/page/13/#content-inner">13</a><a class="extend next" rel="next" href="/page/3/#content-inner"><i class="fas fa-chevron-right fa-fw"></i></a></div></nav></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="is-center"><div class="avatar-img"><img src= "https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/loading.gif" data-lazy-src="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/404/avatar.jpg" onerror="this.onerror=null;this.src='/img/friend_404.gif'" alt="avatar"/></div><div class="author-info__name">liuzhihang</div><div class="author-info__description">学，然后知不足；教，然后知困。</div></div><div class="card-info-data"><div class="card-info-data-item is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">145</div></a></div><div class="card-info-data-item is-center"><a href="/tags/"><div class="headline">标签</div><div class="length-num">55</div></a></div><div class="card-info-data-item is-center"><a href="/categories/"><div class="headline">分类</div><div class="length-num">31</div></a></div></div><a class="button--animated" id="card-info-btn" target="_blank" rel="noopener" href="https://github.com/liuzhihang"><i class="fab fa-github"></i><span>Follow Me</span></a><div class="card-info-social-icons is-center"><a class="social-icon" href="https://github.com/liuzhihang" target="_blank" title="Github"><i class="fab fa-github"></i></a><a class="social-icon" href="http://mail.qq.com/cgi-bin/qm_share?t=qm_mailme&amp;email=Vjo-Iyw_Pz43ODElFicneDU5Ow" target="_blank" title="Email"><i class="fas fa-envelope-open"></i></a><a class="social-icon" href="https://weibo.com/onlyhang" target="_blank" title="Weibo"><i class="fab fa-weibo"></i></a><a class="social-icon" href="https://twitter.com/liuzhihangs" target="_blank" title="Twitter"><i class="fab fa-twitter"></i></a><a class="social-icon" href="/atom.xml" target="_blank" title="RSS"><i class="fas fa-rss"></i></a><a class="social-icon" href="https://www.infoq.cn/u/liuzhihang/publish" target="_blank" title="InfoQ"><i class="iconfont iconweibiaoti-1"></i></a><a class="social-icon" href="https://juejin.im/user/1987506650493117" target="_blank" title="掘金"><i class="iconfont iconjuejin-1"></i></a><a class="social-icon" href="https://blog.csdn.net/qq_36535538" target="_blank" title="CSDN"><i class="iconfont iconcsdn"></i></a><a class="social-icon" href="https://www.zhihu.com/people/liuzhihang" target="_blank" title="知乎"><i class="iconfont iconzhihu1"></i></a><a class="social-icon" href="https://leetcode-cn.com/u/liuzhihang" target="_blank" title="LeetCode"><i class="iconfont iconleetcode"></i></a></div></div><div class="card-widget card-announcement"><div class="item-headline"><i class="fas fa-bullhorn card-announcement-animation"></i><span>公告</span></div><div class="announcement_content">🧑‍💻感谢访问本站，若喜欢请收藏 ^_^ <br> <br> <img src= "https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/loading.gif" data-lazy-src="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/wechat.jpg"> <br> 个人公众号:『 程序员小航 』</div></div><div class="sticky_layout"><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>最新文章</span></div><div class="aside-list"><div class="aside-list-item"><a class="thumbnail" href="/2021/09/04/the_converter_converts_front_end_parameters_to_enumerations.html" title="使用 SpringBoot 转换器将前端参数转换为枚举"><img src= "https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/loading.gif" data-lazy-src="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/uztio4-T5n5Wm.jpg" onerror="this.onerror=null;this.src='https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/feature/92776_n5aac6.jpg'" alt="使用 SpringBoot 转换器将前端参数转换为枚举"/></a><div class="content"><a class="title" href="/2021/09/04/the_converter_converts_front_end_parameters_to_enumerations.html" title="使用 SpringBoot 转换器将前端参数转换为枚举">使用 SpringBoot 转换器将前端参数转换为枚举</a><time datetime="2021-09-03T23:00:00.000Z" title="发表于 2021-09-04 07:00:00">2021-09-04</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2021/08/22/guide_to_mapper_overloading_crater_in_mybatis_plus.html" title="MyBatis-Plus 中 Mapper 重载踩坑指南"><img src= "https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/loading.gif" data-lazy-src="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/ze1d2z-wXDWp0.jpg" onerror="this.onerror=null;this.src='https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/feature/92776_n5aac6.jpg'" alt="MyBatis-Plus 中 Mapper 重载踩坑指南"/></a><div class="content"><a class="title" href="/2021/08/22/guide_to_mapper_overloading_crater_in_mybatis_plus.html" title="MyBatis-Plus 中 Mapper 重载踩坑指南">MyBatis-Plus 中 Mapper 重载踩坑指南</a><time datetime="2021-08-21T23:00:00.000Z" title="发表于 2021-08-22 07:00:00">2021-08-22</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2021/07/25/tips_for_locking_and_optimizing_concurrent_scenes.html" title="并发场景加锁优化小技巧"><img src= "https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/loading.gif" data-lazy-src="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/MODJ8j-9ObHeB.jpg" onerror="this.onerror=null;this.src='https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/feature/92776_n5aac6.jpg'" alt="并发场景加锁优化小技巧"/></a><div class="content"><a class="title" href="/2021/07/25/tips_for_locking_and_optimizing_concurrent_scenes.html" title="并发场景加锁优化小技巧">并发场景加锁优化小技巧</a><time datetime="2021-07-24T23:00:00.000Z" title="发表于 2021-07-25 07:00:00">2021-07-25</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2021/07/16/curator-5.html" title="ZooKeeper 分布式锁 Curator 源码 05：分布式读写锁和联锁"><img src= "https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/loading.gif" data-lazy-src="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/EO0tci-8Ju4bN.png" onerror="this.onerror=null;this.src='https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/feature/92776_n5aac6.jpg'" alt="ZooKeeper 分布式锁 Curator 源码 05：分布式读写锁和联锁"/></a><div class="content"><a class="title" href="/2021/07/16/curator-5.html" title="ZooKeeper 分布式锁 Curator 源码 05：分布式读写锁和联锁">ZooKeeper 分布式锁 Curator 源码 05：分布式读写锁和联锁</a><time datetime="2021-07-16T13:30:30.000Z" title="发表于 2021-07-16 21:30:30">2021-07-16</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2021/07/15/curator-4.html" title="ZooKeeper 分布式锁 Curator 源码 04：分布式信号量和互斥锁"><img src= "https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/loading.gif" data-lazy-src="https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/article/EO0tci-8Ju4bN.png" onerror="this.onerror=null;this.src='https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/feature/92776_n5aac6.jpg'" alt="ZooKeeper 分布式锁 Curator 源码 04：分布式信号量和互斥锁"/></a><div class="content"><a class="title" href="/2021/07/15/curator-4.html" title="ZooKeeper 分布式锁 Curator 源码 04：分布式信号量和互斥锁">ZooKeeper 分布式锁 Curator 源码 04：分布式信号量和互斥锁</a><time datetime="2021-07-15T13:30:30.000Z" title="发表于 2021-07-15 21:30:30">2021-07-15</time></div></div></div></div><div class="card-widget" id="card-newest-comments"><div class="item-headline"><i class="fas fa-comment-dots"></i><span>最新评论</span></div><div class="aside-list"><span>正在加载中...</span></div></div><div class="card-widget card-categories"><div class="item-headline">
            <i class="fas fa-folder-open"></i>
            <span>分类</span>
            <a class="card-more-btn" href="/categories/" title="查看更多">
    <i class="fas fa-angle-right"></i></a>
            </div>
            <ul class="card-category-list" id="aside-cat-list">
            <li class="card-category-list-item "><a class="card-category-list-link" href="/categories/ELK/"><span class="card-category-list-name">ELK</span><span class="card-category-list-count">10</span></a></li><li class="card-category-list-item "><a class="card-category-list-link" href="/categories/IDEA/"><span class="card-category-list-name">IDEA</span><span class="card-category-list-count">4</span></a></li><li class="card-category-list-item parent"><a class="card-category-list-link" href="/categories/Redis/"><span class="card-category-list-name">Redis</span><span class="card-category-list-count">1</span><i class="fas fa-caret-left "></i></a><ul class="card-category-list child"><li class="card-category-list-item "><a class="card-category-list-link" href="/categories/Redis/cache/"><span class="card-category-list-name">cache</span><span class="card-category-list-count">1</span></a></li></ul></li><li class="card-category-list-item "><a class="card-category-list-link" href="/categories/Spring/"><span class="card-category-list-name">Spring</span><span class="card-category-list-count">6</span></a></li><li class="card-category-list-item "><a class="card-category-list-link" href="/categories/docker/"><span class="card-category-list-name">docker</span><span class="card-category-list-count">1</span></a></li><li class="card-category-list-item "><a class="card-category-list-link" href="/categories/hexo/"><span class="card-category-list-name">hexo</span><span class="card-category-list-count">3</span></a></li><li class="card-category-list-item "><a class="card-category-list-link" href="/categories/issue/"><span class="card-category-list-name">issue</span><span class="card-category-list-count">9</span></a></li>
            </ul></div><div class="card-widget card-tags"><div class="item-headline"><i class="fas fa-tags"></i><span>标签</span></div><div class="card-tag-cloud"><a href="/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/" style="font-size: 1.24em; color: rgb(27, 61, 172)">多线程</a><a href="/tags/%E7%BA%BF%E7%A8%8B%E6%B1%A0/" style="font-size: 1.15em; color: rgb(31, 46, 195)">线程池</a><a href="/tags/%E5%88%86%E5%B8%83%E5%BC%8F/" style="font-size: 1.3em; color: rgb(136, 63, 41)">分布式</a><a href="/tags/volatile/" style="font-size: 1.15em; color: rgb(78, 150, 108)">volatile</a><a href="/tags/%E6%B5%81%E6%B0%B4%E5%8F%B7/" style="font-size: 1.15em; color: rgb(97, 116, 54)">流水号</a><a href="/tags/%E9%9B%AA%E8%8A%B1%E7%AE%97%E6%B3%95/" style="font-size: 1.15em; color: rgb(153, 3, 140)">雪花算法</a><a href="/tags/%E5%B7%A5%E4%BD%9C%E7%AC%94%E8%AE%B0/" style="font-size: 1.36em; color: rgb(71, 56, 184)">工作笔记</a><a href="/tags/ELK/" style="font-size: 1.39em; color: rgb(173, 103, 93)">ELK</a><a href="/tags/mysql/" style="font-size: 1.27em; color: rgb(158, 27, 189)">mysql</a><a href="/tags/docker/" style="font-size: 1.15em; color: rgb(49, 71, 145)">docker</a><a href="/tags/hexo/" style="font-size: 1.21em; color: rgb(39, 159, 158)">hexo</a><a href="/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" style="font-size: 1.27em; color: rgb(121, 128, 34)">设计模式</a><a href="/tags/%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/" style="font-size: 1.18em; color: rgb(71, 16, 19)">单例模式</a><a href="/tags/issue/" style="font-size: 1.27em; color: rgb(103, 35, 165)">issue</a><a href="/tags/error/" style="font-size: 1.15em; color: rgb(187, 170, 18)">error</a><a href="/tags/ip/" style="font-size: 1.15em; color: rgb(110, 109, 195)">ip</a><a href="/tags/mac/" style="font-size: 1.21em; color: rgb(111, 162, 110)">mac</a><a href="/tags/brew/" style="font-size: 1.15em; color: rgb(16, 73, 142)">brew</a><a href="/tags/software/" style="font-size: 1.15em; color: rgb(99, 166, 64)">software</a><a href="/tags/%E5%B7%A5%E5%85%B7%E5%86%8C/" style="font-size: 1.33em; color: rgb(85, 49, 104)">工具册</a><a href="/tags/markdown/" style="font-size: 1.15em; color: rgb(4, 182, 111)">markdown</a><a href="/tags/%E5%B0%8F%E6%8A%80%E5%B7%A7/" style="font-size: 1.15em; color: rgb(87, 63, 123)">小技巧</a><a href="/tags/plugin/" style="font-size: 1.27em; color: rgb(112, 41, 166)">plugin</a><a href="/tags/IDEA/" style="font-size: 1.15em; color: rgb(58, 7, 103)">IDEA</a><a href="/tags/next-key/" style="font-size: 1.24em; color: rgb(35, 44, 24)">next-key</a><a href="/tags/interceptor/" style="font-size: 1.15em; color: rgb(16, 136, 140)">interceptor</a><a href="/tags/Spring/" style="font-size: 1.42em; color: rgb(51, 65, 71)">Spring</a><a href="/tags/servlet/" style="font-size: 1.15em; color: rgb(34, 102, 91)">servlet</a><a href="/tags/%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86/" style="font-size: 1.18em; color: rgb(200, 176, 26)">动态代理</a><a href="/tags/aop/" style="font-size: 1.15em; color: rgb(89, 102, 61)">aop</a><a href="/tags/JWT/" style="font-size: 1.15em; color: rgb(39, 109, 92)">JWT</a><a href="/tags/SpringSecurity/" style="font-size: 1.15em; color: rgb(146, 37, 199)">SpringSecurity</a><a href="/tags/transactional/" style="font-size: 1.15em; color: rgb(37, 63, 196)">transactional</a><a href="/tags/Redis/" style="font-size: 1.15em; color: rgb(53, 73, 15)">Redis</a><a href="/tags/cache/" style="font-size: 1.15em; color: rgb(147, 90, 144)">cache</a><a href="/tags/%E9%93%BE%E8%B7%AF%E8%BF%BD%E8%B8%AA/" style="font-size: 1.15em; color: rgb(27, 108, 31)">链路追踪</a><a href="/tags/Archetype/" style="font-size: 1.15em; color: rgb(131, 37, 136)">Archetype</a><a href="/tags/Doc-View/" style="font-size: 1.3em; color: rgb(108, 91, 182)">Doc View</a><a href="/tags/skywalking/" style="font-size: 1.15em; color: rgb(78, 177, 5)">skywalking</a><a href="/tags/%E6%BA%90%E7%A0%81%E7%AC%94%E8%AE%B0/" style="font-size: 1.45em; color: rgb(10, 188, 19)">源码笔记</a></div></div><div class="card-widget card-archives"><div class="item-headline"><i class="fas fa-archive"></i><span>归档</span><a class="card-more-btn" href="/archives/" title="查看更多">
    <i class="fas fa-angle-right"></i></a></div><ul class="card-archive-list"><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2021/09/"><span class="card-archive-list-date">九月 2021</span><span class="card-archive-list-count">1</span></a></li><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2021/08/"><span class="card-archive-list-date">八月 2021</span><span class="card-archive-list-count">1</span></a></li><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2021/07/"><span class="card-archive-list-date">七月 2021</span><span class="card-archive-list-count">14</span></a></li><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2021/06/"><span class="card-archive-list-date">六月 2021</span><span class="card-archive-list-count">9</span></a></li><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2021/05/"><span class="card-archive-list-date">五月 2021</span><span class="card-archive-list-count">1</span></a></li><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2021/04/"><span class="card-archive-list-date">四月 2021</span><span class="card-archive-list-count">4</span></a></li><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2021/03/"><span class="card-archive-list-date">三月 2021</span><span class="card-archive-list-count">1</span></a></li><li class="card-archive-list-item"><a class="card-archive-list-link" href="/archives/2021/02/"><span class="card-archive-list-date">二月 2021</span><span class="card-archive-list-count">3</span></a></li></ul></div><div class="card-widget card-webinfo"><div class="item-headline"><i class="fas fa-chart-line"></i><span>网站资讯</span></div><div class="webinfo"><div class="webinfo-item"><div class="item-name">文章数目 :</div><div class="item-count">145</div></div><div class="webinfo-item"><div class="item-name">本站总字数 :</div><div class="item-count">148.2k</div></div><div class="webinfo-item"><div class="item-name">本站访客数 :</div><div class="item-count" id="busuanzi_value_site_uv"></div></div><div class="webinfo-item"><div class="item-name">本站总访问量 :</div><div class="item-count" id="busuanzi_value_site_pv"></div></div><div class="webinfo-item"><div class="item-name">最后更新时间 :</div><div class="item-count" id="last-push-date" data-lastPushDate="2021-09-08T09:05:43.976Z"></div></div></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2017 - 2021 By liuzhihang</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div><div class="footer_custom_text"><a target="_blank" rel="noopener" href="http://www.beian.miit.gov.cn/"><img class="icp-icon" src= "https://cdn.jsdelivr.net/gh/liuzhihang/oss/pic/loading.gif" data-lazy-src="/resources/image/icp.png"><span>备案号：京ICP备20000888号</span></a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="translateLink" type="button" title="简繁转换">繁</button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div id="local-search"><div class="search-dialog"><div class="search-dialog__title" id="local-search-title">本地搜索</div><div id="local-input-panel"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div></div><hr/><div id="local-search-results"></div><span class="search-close-button"><i class="fas fa-times"></i></span></div><div id="search-mask"></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="/js/tw_cn.js"></script><script src="https://cdn.jsdelivr.net/npm/instant.page/instantpage.min.js" type="module"></script><script src="https://cdn.jsdelivr.net/npm/vanilla-lazyload/dist/lazyload.iife.min.js"></script><script src="/js/search/local-search.js"></script><div class="js-pjax"><script>if (document.getElementsByClassName('mermaid').length) {
  if (window.mermaidJsLoad) mermaid.init()
  else {
    getScript('https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js').then(() => {
      window.mermaidJsLoad = true
      mermaid.initialize({
        theme: 'default',
      })
      false && mermaid.init()
    })
  }
}</script></div><script>window.addEventListener('load', () => {
  const changeContent = (content) => {
    if (content === '') return content

    content = content.replace(/<img.*?src="(.*?)"?[^\>]+>/ig, '[图片]') // replace image link
    content = content.replace(/<a[^>]+?href=["']?([^"']+)["']?[^>]*>([^<]+)<\/a>/gi, '[链接]') // replace url
    content = content.replace(/<pre><code>.*?<\/pre>/gi, '[代码]') // replace code
    content = content.replace(/<[^>]+>/g,"") // remove html tag

    if (content.length > 150) {
      content = content.substring(0,150) + '...'
    }
    return content
  }

  const findTrueUrl = (array) => {
    Promise.all(array.map(item =>
      fetch(item.url).then(resp => resp.json()).then(data => {
        const urlArray = data.body.match(/(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?/ig)
        if (data.user.login === 'utterances-bot') {
          return urlArray.pop()
        } else {
          return urlArray.shift()
        }
      })
    )).then(res => {
        array = array.map((i,index)=> {
          return {
            ...i,
            url: res[index]
          }
        })

        saveToLocal.set('github-newest-comments', JSON.stringify(array), 10/(60*24))
        generateHtml(array)
    });
  }

  const getComment = () => {
    fetch('https://api.github.com/repos/liuzhihang/comments/issues/comments?sort=updated&direction=desc&per_page=6&page=1',{
      "headers": {
        Accept: 'application/vnd.github.v3.html+json'
      }
    })
      .then(response => response.json())
      .then(data => {
        const githubArray = data.map(item => {
          return {
            'avatar': item.user.avatar_url,
            'content': changeContent(item.body_html),
            'nick': item.user.login,
            'url': item.issue_url,
            'date': item.updated_at,
            'githubUrl': item.html_url
          }
        })
        findTrueUrl(githubArray)
      }).catch(e => {
        const $dom = document.querySelector('#card-newest-comments .aside-list')
        $dom.innerHTML= "无法获取评论，请确认相关配置是否正确"
      })
  }

  const generateHtml = array => {
    let result = ''

    if (array.length) {
      for (let i = 0; i < array.length; i++) {
        result += '<div class=\'aside-list-item\'>'

        if (true) {
          const name = 'data-lazy-src'
          result += `<a href='${array[i].url}' class='thumbnail'><img ${name}='${array[i].avatar}' alt='${array[i].nick}'></a>`
        }

        result += `<div class='content'>
        <a class='comment' href='${array[i].url}'>${array[i].content}</a>
        <div class='name'><span>${array[i].nick} / </span><time datetime="${array[i].date}">${btf.diffDate(array[i].date, true)}</time></div>
        </div></div>`
      }
    } else {
      result += '没有评论'
    }

    let $dom = document.querySelector('#card-newest-comments .aside-list')
    $dom.innerHTML= result
    window.lazyLoadInstance && window.lazyLoadInstance.update()
    window.pjax && window.pjax.refresh($dom)
  }

  const newestCommentInit = () => {
    if (document.querySelector('#card-newest-comments .aside-list')) {
      const data = saveToLocal.get('github-newest-comments')
      if (data) {
        generateHtml(JSON.parse(data))
      } else {
        getComment()
      }
    }
  }

  newestCommentInit()
  document.addEventListener('pjax:complete', newestCommentInit)
})</script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div></body></html>